题解 P2048 【[NOI2010]超级钢琴】

$Description$

给$n$个数,求数字和前$k$大的区间的和

$Solution$

我们考虑先找全局最大值,再删去全局最大值,找次大值,然后删去,直到找到第$k$大值。

枚举左端点,此时可行的右端点是一个区间$[i+L-1,min(i+R-1,n)]$,可以搞一个结构体$\left( i,i+L-1,min(i+R-1,n),\max\limits_{j=i+L-1}^{min(i+R-1,n)}sum[j],loc\right),loc$表示$max\;sum[j]$的位置$j,$然后把它扔到堆里,堆中比较$\max\limits_{j=i+L-1}^{min(i+R-1,n)}sum[j]-sum[i-1]$

全局最大值取堆顶${x,l,r,mx,loc}$,然后弹出,放入$\left(x,l,loc-1,\max\limits_{j=l}^{loc-1}sum[j],nloc_1\right)$和
$\left(x,loc+1,r,\max\limits_{j=loc+1}^{r}sum[j],nloc_2\right)$

这个东西显然是对的

$Code$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define re register
#define N 500200
using namespace std;
int lg[N],n,m,L,R;
ll ans,sum[N];
struct node{
int v,loc;
friend bool operator <(node a,node b){
return a.v<b.v;
}
}f[N][21];
struct rec{
int x,l,r;node v;
friend bool operator <(rec a,rec b){
return a.v.v-sum[a.x-1]<b.v.v-sum[b.x-1];
}
};
priority_queue<rec>q;
inline int read(){
int x=0,w=0;char ch=getchar();
while (!isdigit(ch))w|=ch=='-',ch=getchar();
while (isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return w?-x:x;
}
node query(int l,int r){
int s=lg[r-l+1];
return max(f[l][s],f[r-(1<<s)+1][s]);
}
signed main(){
n=read(),m=read(),L=read(),R=read();lg[0]=-1;
for (int i=1;i<=n;++i)
f[i][0]=(node){sum[i]=sum[i-1]+read(),i},lg[i]=lg[i>>1]+1;
for (int j=1;j<=20;++j)
for (int i=1;i+(1<<j)-1<=n;++i)
f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);
for (int i=1;i<=n;++i)
if (i+L-1<=n)
q.push((rec){i,i+L-1,min(i+R-1,n),query(i+L-1,min(i+R-1,n))});
while (m--){
rec u=q.top();q.pop();
if (u.l<u.v.loc)q.push((rec){u.x,u.l,u.v.loc-1,query(u.l,u.v.loc-1)});
if (u.v.loc<u.r)q.push((rec){u.x,u.v.loc+1,u.r,query(u.v.loc+1,u.r)});
ans+=u.v.v-sum[u.x-1];
}
printf("%lld\n",ans);
return 0;
}